/*
 * Copyright (c) 2022 HiSilicon (Shanghai) Technologies CO., LIMITED.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <pthread.h>
#include <errno.h>
#include <signal.h>
#include <limits.h>

#include "audio_rtmp.h"

static PAYLOAD_TYPE_E gs_enPayloadType = PT_AAC;
static HI_BOOL gs_bAioReSample = HI_FALSE;
// static HI_BOOL gs_bUserGetMode = HI_FALSE;
static HI_BOOL gs_bUserGetMode = HI_TRUE;
static HI_BOOL gs_bAoVolumeCtrl = HI_FALSE;
static AUDIO_SAMPLE_RATE_E g_in_sample_rate = AUDIO_SAMPLE_RATE_BUTT;
static AUDIO_SAMPLE_RATE_E g_out_sample_rate = AUDIO_SAMPLE_RATE_BUTT;
/* 0: close, 1: record, 2:reserved, 3:talkv2 */
static HI_U32 g_ai_vqe_type = 3;
static HI_BOOL g_sample_audio_exit = HI_FALSE;

static AI_RECORDVQE_CONFIG_S g_ai_record_vqe_attr;
static AI_TALKVQEV2_CONFIG_S g_ai_talkv2_vqe_attr;

#define SAMPLE_DBG(s32Ret)                                                          \
    do {                                                                            \
        printf("s32Ret = %#x, fuc:%s, line:%d\n", s32Ret, __FUNCTION__, __LINE__);  \
    } while (0)

static getstop( HI_VOID )
{
    while ( g_sample_audio_exit == HI_FALSE )
    {
        hi_usleep(10);
    }
    printf("[INFO] : [SAMPLE_AUDIO_PROCESS] exit audio process pthread\n");
}

/* function : PT Number to String */
static char *SAMPLE_AUDIO_Pt2Str(PAYLOAD_TYPE_E enType)
{
    if (enType == PT_G711A) {
        return "g711a";
    } else if (enType == PT_G711U) {
        return "g711u";
    } else if (enType == PT_ADPCMA) {
        return "adpcm";
    } else if (enType == PT_G726) {
        return "g726";
    } else if (enType == PT_LPCM) {
        return "pcm";
    } else if (enType == PT_AAC) {
        return "aac";
    } else if (enType == PT_MP3) {
        return "mp3";
    } else {
        return "data";
    }
}

static FILE *SAMPLE_AUDIO_OpenAencFile(AENC_CHN AeChn, PAYLOAD_TYPE_E enType)
{
    FILE *pfd = NULL;
    HI_CHAR aszFileName[FILE_NAME_LEN] = {0};
    HI_S32 s32Ret;
    /* create file for save stream */
#if defined(__HuaweiLite__) && (!defined(__OHOS__))
    s32Ret = snprintf_s(aszFileName, FILE_NAME_LEN, FILE_NAME_LEN - 1, "/sharefs/audio_chn%d.%s", AeChn,
        SAMPLE_AUDIO_Pt2Str(enType));
    if (s32Ret <= EOK) {
        printf("\n snprintf_s fail! ret = 0x%x", s32Ret);
        return NULL;
    }
#else
    s32Ret = snprintf_s(aszFileName, FILE_NAME_LEN, FILE_NAME_LEN - 1,
        "audio_chn%d.%s", AeChn, SAMPLE_AUDIO_Pt2Str(enType));
    if (s32Ret <= EOK) {
        printf("\n snprintf_s fail! ret = 0x%x", s32Ret);
        return NULL;
    }
#endif
    pfd = fopen(aszFileName, "w+");
    if (pfd == NULL) {
        printf("%s: open file %s failed\n", __FUNCTION__, aszFileName);
        return NULL;
    }
    printf("open stream file:\"%s\" for aenc ok\n", aszFileName);
    return pfd;
}

static hi_void sample_audio_ai_aenc_init_param(AIO_ATTR_S *attr, hi_void **ai_vqe_attr)
{
    attr->enSamplerate = AUDIO_SAMPLE_RATE_48000;
    attr->enBitwidth = AUDIO_BIT_WIDTH_16;
    attr->enWorkmode = AIO_MODE_I2S_MASTER;
    attr->enSoundmode = AUDIO_SOUND_MODE_STEREO; // AUDIO_SOUND_MODE_STEREO;
    attr->u32EXFlag = 0;
    attr->u32FrmNum = FPS_30;
    attr->u32ChnCnt = 2; // 2; /* 2: chn num */
    attr->u32ClkSel = 1;
    attr->enI2sType = AIO_I2STYPE_INNERCODEC;
    attr->u32PtNumPerFrm = AACLC_SAMPLES_PER_FRAME;

    gs_bAioReSample = HI_FALSE;
    g_in_sample_rate = AUDIO_SAMPLE_RATE_BUTT;
    g_out_sample_rate = AUDIO_SAMPLE_RATE_BUTT;

    if (g_ai_vqe_type == 1) { /* 1: Record VQE */
        (hi_void)memset_s(&g_ai_record_vqe_attr, sizeof(AI_RECORDVQE_CONFIG_S), 0, sizeof(AI_RECORDVQE_CONFIG_S));
        g_ai_record_vqe_attr.s32WorkSampleRate = AUDIO_SAMPLE_RATE_48000;
        g_ai_record_vqe_attr.s32FrameSample = SAMPLE_AUDIO_PTNUMPERFRM;
        g_ai_record_vqe_attr.enWorkstate = VQE_WORKSTATE_COMMON;
        g_ai_record_vqe_attr.s32InChNum = 2; /* 1: chn num */
        g_ai_record_vqe_attr.s32OutChNum = 2; /* 1: chn num */
        g_ai_record_vqe_attr.enRecordType = VQE_RECORD_NORMAL;
        g_ai_record_vqe_attr.stDrcCfg.bUsrMode = HI_FALSE;
        g_ai_record_vqe_attr.stRnrCfg.bUsrMode = HI_FALSE;
        g_ai_record_vqe_attr.stHdrCfg.bUsrMode = HI_FALSE;
        g_ai_record_vqe_attr.stAgcCfg.bUsrMode = HI_FALSE;
        g_ai_record_vqe_attr.stHpfCfg.bUsrMode = HI_TRUE;
        g_ai_record_vqe_attr.stHpfCfg.enHpfFreq = AUDIO_HPF_FREQ_80;
        g_ai_record_vqe_attr.u32OpenMask =
            AI_RECORDVQE_MASK_DRC | AI_RECORDVQE_MASK_HDR | AI_RECORDVQE_MASK_HPF | AI_RECORDVQE_MASK_RNR;
        // g_ai_record_vqe_attr.u32OpenMask =
        //     AI_RECORDVQE_MASK_AGC | AI_RECORDVQE_MASK_RNR;
        *ai_vqe_attr = (hi_void *)&g_ai_record_vqe_attr;
    } else if (g_ai_vqe_type == 3) { /* 3: TalkV2 VQE */
        (hi_void)memset_s(&g_ai_talkv2_vqe_attr, sizeof(AI_TALKVQEV2_CONFIG_S), 0, sizeof(AI_TALKVQEV2_CONFIG_S));
        g_ai_talkv2_vqe_attr.s32WorkSampleRate = AUDIO_SAMPLE_RATE_16000;
        g_ai_talkv2_vqe_attr.s32FrameSample = SAMPLE_AUDIO_PTNUMPERFRM;
        g_ai_talkv2_vqe_attr.enWorkstate = VQE_WORKSTATE_COMMON;
        g_ai_talkv2_vqe_attr.s32InChNum = 2; /* 1: chn num */
        g_ai_talkv2_vqe_attr.s32OutChNum = 2; /* 1: chn num */

        g_ai_talkv2_vqe_attr.stPnrCfg.bUsrMode = HI_TRUE;

        g_ai_talkv2_vqe_attr.stPnrCfg.s16MinGainLimit = 2827;     /* maximum allowable noise rejection, default:5827, range:[0, 32767] */
        g_ai_talkv2_vqe_attr.stPnrCfg.s16SnrPriorLimit = 1276;    /* minimum prior signal-to-noise ratio, default:3276, range:[0, 32767] */
        g_ai_talkv2_vqe_attr.stPnrCfg.s16HtThreshold = 10;      /* voice decision threshold, default:10, range:[0, 80] */
        g_ai_talkv2_vqe_attr.stPnrCfg.s16HsThreshold = 100;      /* harmonic detection threshold, default:100, range:[100, 1100] */
        g_ai_talkv2_vqe_attr.stPnrCfg.s16AlphaPh = 90;          /* speech probability smoothing coefficient, default:90, range:[0, 100] */
        g_ai_talkv2_vqe_attr.stPnrCfg.s16AlphaPsd = 50;         /* noise estimation smoothing factor, default:65, range:[0, 100] */
        g_ai_talkv2_vqe_attr.stPnrCfg.s16PriorSnrFixed = 30;    /* fixed prior signal-to-noise ratio, default:30, range:[1, 499] */
        g_ai_talkv2_vqe_attr.stPnrCfg.s16CepThreshold = 16;     /* cepstrum smooth speech threshold, default:16, range:[0, 100] */
        g_ai_talkv2_vqe_attr.stPnrCfg.s16SpeechProtectThreshold = 90;   /* speech protect threshold, default:90, range:[0, 100] */
        g_ai_talkv2_vqe_attr.stPnrCfg.s16HemEnable = 1;        /* harmonic enhancement enable, default:1, range:[0, 1] */
        g_ai_talkv2_vqe_attr.stPnrCfg.s16TcsEnable = 0;        /* cepstral smoothing switch, default:0, range:[0, 1] */

        // for (int i = 0; i < 30; i++)
        // {
        //     g_ai_talkv2_vqe_attr.stEqCfg.s8GaindB[i] = -50;
        // }
        // g_ai_talkv2_vqe_attr.stEqCfg.s8GaindB[0] = -30;
        // g_ai_talkv2_vqe_attr.stEqCfg.s8GaindB[1] = 0;
        // g_ai_talkv2_vqe_attr.stEqCfg.s8GaindB[2] = 5;
        // g_ai_talkv2_vqe_attr.stEqCfg.s8GaindB[3] = 15;
        // g_ai_talkv2_vqe_attr.stEqCfg.s8GaindB[4] = 20;
        // g_ai_talkv2_vqe_attr.stEqCfg.s8GaindB[5] = 20;
        // g_ai_talkv2_vqe_attr.stEqCfg.s8GaindB[6] = 20;
        // g_ai_talkv2_vqe_attr.stEqCfg.s8GaindB[7] = 15;
        // g_ai_talkv2_vqe_attr.stEqCfg.s8GaindB[8] = 0;
        // g_ai_talkv2_vqe_attr.stEqCfg.s8GaindB[9] = -15;
        // g_ai_talkv2_vqe_attr.u32OpenMask = AI_TALKVQEV2_MASK_EQ | AI_TALKVQEV2_MASK_PNR | AI_TALKVQEV2_MASK_LIMITER;
        g_ai_talkv2_vqe_attr.u32OpenMask = AI_TALKVQEV2_MASK_PNR;
        *ai_vqe_attr = (hi_void *)&g_ai_talkv2_vqe_attr;
    } else {
        *ai_vqe_attr = HI_NULL;
    }
}

static HI_VOID SAMPLE_AUDIO_StartAdecAo(HI_BOOL bSendAdec, AUDIO_DEV AoDev, AENC_CHN AeChn, AIO_ATTR_S *pstAioAttr)
{
    HI_S32 s32Ret, s32AoChnCnt;
    const AO_CHN AoChn = 0;
    const ADEC_CHN AdChn = 0;
    FILE *pfd = NULL;

    if (bSendAdec == HI_TRUE) {
        s32Ret = SAMPLE_COMM_AUDIO_StartAdec(AdChn, gs_enPayloadType);
        if (s32Ret != HI_SUCCESS) {
            return;
        }

        s32AoChnCnt = pstAioAttr->u32ChnCnt;
        s32Ret = SAMPLE_COMM_AUDIO_StartAo(AoDev, s32AoChnCnt, pstAioAttr, g_in_sample_rate, gs_bAioReSample);
        if (s32Ret != HI_SUCCESS) {
            goto STOP_ADEC;
        }

        pfd = SAMPLE_AUDIO_OpenAencFile(AdChn, gs_enPayloadType);
        if (pfd == NULL) {
            SAMPLE_DBG(HI_FAILURE);
            goto STOP_AO;
        }
        s32Ret = SAMPLE_COMM_AUDIO_CreateTrdAencAdec(AeChn, AdChn, pfd);
        if (s32Ret != HI_SUCCESS) {
            (HI_VOID)fclose(pfd);
            pfd = HI_NULL;
            SAMPLE_DBG(s32Ret);
            goto STOP_AO;
        }

        s32Ret = SAMPLE_COMM_AUDIO_AoBindAdec(AoDev, AoChn, AdChn);
        if (s32Ret != HI_SUCCESS) {
            SAMPLE_DBG(s32Ret);
            goto DESTORY_AENC_THREAD;
        }

        printf("bind adec:%d to ao(%d,%d) ok \n", AdChn, AoDev, AoChn);
    }

    /* step while : use while for stop the process */
    getstop();

    if (bSendAdec == HI_TRUE) {
        s32Ret = SAMPLE_COMM_AUDIO_AoUnbindAdec(AoDev, AoChn, AdChn);
        if (s32Ret != HI_SUCCESS) {
            SAMPLE_DBG(s32Ret);
        }

    DESTORY_AENC_THREAD:
        (HI_VOID)SAMPLE_COMM_AUDIO_DestroyTrdAencAdec(AdChn);

    STOP_AO:
        (HI_VOID)SAMPLE_COMM_AUDIO_StopAo(AoDev, s32AoChnCnt, gs_bAioReSample);

    STOP_ADEC:
        (HI_VOID)SAMPLE_COMM_AUDIO_StopAdec(AdChn);
    }
}

static HI_VOID SAMPLE_AUDIO_StartAdec(HI_BOOL bSendAdec, AUDIO_DEV AoDev, AENC_CHN AeChn, AIO_ATTR_S *pstAioAttr)
{
    HI_S32 s32Ret;
    const AO_CHN AoChn = 0;
    const ADEC_CHN AdChn = 0;
    FILE *pfd = NULL;

    if (bSendAdec == HI_TRUE) {

        pfd = SAMPLE_AUDIO_OpenAencFile(AdChn, gs_enPayloadType);
        if (pfd == NULL) {
            SAMPLE_DBG(HI_FAILURE);
            return;
        }

        s32Ret = SAMPLE_COMM_AUDIO_CreateTrdAencAdec(AeChn, AdChn, pfd);
        if (s32Ret != HI_SUCCESS) {
            (HI_VOID)fclose(pfd);
            pfd = HI_NULL;
            SAMPLE_DBG(s32Ret);
            return;
        }
    }

    /* step while : use while for stop the process */
    getstop();

    if (bSendAdec == HI_TRUE) {
        (HI_VOID)SAMPLE_COMM_AUDIO_DestroyTrdAencAdec(AdChn);
    }
}

static HI_VOID SAMPLE_AUDIO_AiAencInner(AUDIO_DEV AiDev, AUDIO_DEV AoDev, HI_S32 s32AencChnCnt, AIO_ATTR_S *pstAioAttr)
{
    HI_S32 i, j, s32Ret;
    AI_CHN AiChn;
    AENC_CHN AeChn = 0;
    HI_BOOL bSendAdec = HI_TRUE;

    for (i = 0; i < s32AencChnCnt; i++) {
        AeChn = i;
        AiChn = i;

        if (gs_bUserGetMode == HI_TRUE) {
            s32Ret = SAMPLE_COMM_AUDIO_CreateTrdAiAenc(AiDev, AiChn, AeChn);
        } else {
            s32Ret = SAMPLE_COMM_AUDIO_AencBindAi(AiDev, AiChn, AeChn);
        }
        if (s32Ret != HI_SUCCESS) {
            SAMPLE_DBG(s32Ret);
            goto EXIT;
        }
        printf("Ai(%d,%d) bind to AencChn:%d ok!\n", AiDev, AiChn, AeChn);
    }

    if (gs_bUserGetMode == HI_TRUE) {
        SAMPLE_AUDIO_StartAdec(bSendAdec, AoDev, AeChn, pstAioAttr);
    } else {
        SAMPLE_AUDIO_StartAdecAo(bSendAdec, AoDev, AeChn, pstAioAttr);
    }

    for (i = 0; i < s32AencChnCnt; i++) {
        AeChn = i;
        AiChn = i;

        if (gs_bUserGetMode == HI_TRUE) {
            s32Ret = SAMPLE_COMM_AUDIO_DestroyTrdAi(AiDev, AiChn);
        } else {
            s32Ret = SAMPLE_COMM_AUDIO_AencUnbindAi(AiDev, AiChn, AeChn);
        }
        if (s32Ret != HI_SUCCESS) {
            SAMPLE_DBG(s32Ret);
        }
    }
    return;

EXIT:
    for (j = 0; j < i; j++) {
        if (gs_bUserGetMode == HI_TRUE) {
            SAMPLE_COMM_AUDIO_DestroyTrdAi(AiDev, j);
        } else {
            SAMPLE_COMM_AUDIO_AencUnbindAi(AiDev, j, j);
        }
    }
    return;
}

/* function : Ai -> Aenc -> file -> Adec -> Ao */
static HI_S32 SAMPLE_AUDIO_AiAenc(HI_VOID)
{
    HI_S32 s32Ret;
    HI_S32 s32AiChnCnt;
    HI_S32 s32AencChnCnt;
    AIO_ATTR_S stAioAttr;
    AUDIO_DEV AiDev = SAMPLE_AUDIO_INNER_AI_DEV;
    AUDIO_DEV AoDev = SAMPLE_AUDIO_INNER_AO_DEV;
    HI_VOID *pAiVqeAttr = NULL;

    sample_audio_ai_aenc_init_param(&stAioAttr, &pAiVqeAttr);

    /* step 1: start Ai */
    s32AiChnCnt = stAioAttr.u32ChnCnt;
    s32Ret = SAMPLE_COMM_AUDIO_StartAi(AiDev, s32AiChnCnt, &stAioAttr, g_out_sample_rate, gs_bAioReSample, pAiVqeAttr, g_ai_vqe_type);
    if (s32Ret != HI_SUCCESS) {
        SAMPLE_DBG(s32Ret);
        goto EXIT;
    }

    /* step 2: config audio codec */
    s32Ret = SAMPLE_COMM_AUDIO_CfgAcodec(&stAioAttr);
    if (s32Ret != HI_SUCCESS) {
        SAMPLE_DBG(s32Ret);
        goto STOP_AI;
    }

    /* step 3: start Aenc */
    s32AencChnCnt = stAioAttr.u32ChnCnt >> (HI_U32)stAioAttr.enSoundmode;
    s32Ret = SAMPLE_COMM_AUDIO_StartAenc(s32AencChnCnt, &stAioAttr, gs_enPayloadType);
    if (s32Ret != HI_SUCCESS) {
        SAMPLE_DBG(s32Ret);
        goto STOP_AI;
    }

    SAMPLE_AUDIO_AiAencInner(AiDev, AoDev, s32AencChnCnt, &stAioAttr);

    s32Ret = SAMPLE_COMM_AUDIO_StopAenc(s32AencChnCnt);
    if (s32Ret != HI_SUCCESS) {
        SAMPLE_DBG(s32Ret);
    }

STOP_AI:
    s32Ret = SAMPLE_COMM_AUDIO_StopAi(AiDev, s32AiChnCnt, gs_bAioReSample, HI_FALSE);
    if (s32Ret != HI_SUCCESS) {
        SAMPLE_DBG(s32Ret);
    }

EXIT:
    return s32Ret;
}

HI_VOID SAMPLE_AUDIO_AiAenc_HandleSig( void )
{
    g_sample_audio_exit = HI_TRUE;
}

/* function : audio aenc process entry */
HI_VOID* SAMPLE_COMM_IVE_StartAiAencAdecAo(HI_VOID* pArgs)
{
    hi_unused(pArgs);

    /* step 1: register encoder and decorder */
    HI_MPI_AENC_AacInit();
    if (gs_bUserGetMode == HI_FALSE)
    {
        HI_MPI_ADEC_AacInit();
    }

    /* step 2: create audio ai encode pthread */
    g_sample_audio_exit = HI_FALSE;
    SAMPLE_AUDIO_AiAenc();

    HI_MPI_AENC_AacDeInit();
    if (gs_bUserGetMode == HI_FALSE)
    {
        HI_MPI_ADEC_AacDeInit();
    }

    return HI_NULL;
}
